//
//  SHOperationViewController.m
//  SDemo
//
//  Created by gg on 2018/7/17.
//  Copyright © 2018年 gg. All rights reserved.
//

#import "SHOperationViewController.h"

@interface SHOperationViewController ()

@property(nonatomic,strong) NSOperationQueue *opQueue;
@property(nonatomic,assign) NSInteger count;
@property(nonatomic,strong) NSLock *lock;

@end

@implementation SHOperationViewController

-(NSOperationQueue *)opQueue {
    if (_opQueue == nil) {
        _opQueue = [[NSOperationQueue alloc] init];
    }
    return  _opQueue;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"Operation";
    self.view.backgroundColor = [UIColor whiteColor];
    
   
    // [self initData];
    // [self useInvocation];
    // [NSThread detachNewThreadSelector:@selector(p_invocationFunc) toTarget:self withObject:nil]; //子线程
//    [self useBlock];
    //[self addOperationWithBlockToQueue];
    
   // [self setMaxConcurrentOperationCount];
    
//    [self addDependency];
//    [self communication];
    [self thredSafy];
    
}

-(void)useInvocation{
    // 输出的是当前的线程
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(p_invocationFunc) object:nil];
    [operation start];
    

    
}

-(void)useBlock {
    
    /**

     输出结果：
    
     2018-07-31 17:40:17.478866+0800 SDemo[39474:1191222] block4 -- <NSThread: 0x6000004792c0>{number = 5, name = (null)}
     2018-07-31 17:40:17.478867+0800 SDemo[39474:1191110] block -- <NSThread: 0x604000065a40>{number = 1, name = main}
     2018-07-31 17:40:17.478881+0800 SDemo[39474:1191219] block3 -- <NSThread: 0x600000464980>{number = 3, name = (null)}
     2018-07-31 17:40:17.478900+0800 SDemo[39474:1191226] block1 -- <NSThread: 0x600000464880>{number = 4, name = (null)}
     2018-07-31 17:40:19.479852+0800 SDemo[39474:1191222] block4 -- <NSThread: 0x6000004792c0>{number = 5, name = (null)}
     2018-07-31 17:40:19.480122+0800 SDemo[39474:1191226] block1 -- <NSThread: 0x600000464880>{number = 4, name = (null)}
     2018-07-31 17:40:19.480144+0800 SDemo[39474:1191110] block -- <NSThread: 0x604000065a40>{number = 1, name = main}
     2018-07-31 17:40:19.480168+0800 SDemo[39474:1191219] block3 -- <NSThread: 0x600000464980>{number = 3, name = (null)}
     
     */
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"block -- %@",[NSThread currentThread]);//在哪个线程调用方法，就在打印哪个线程
        }
    }];
    
    //addExecutionBlock 添加额外的操作 会开辟子线程
    [operation addExecutionBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"block1 -- %@",[NSThread currentThread]);
        }
    }];
    
    [operation addExecutionBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"block4 -- %@",[NSThread currentThread]);
        }
    }];
    
    [operation addExecutionBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"block3 -- %@",[NSThread currentThread]);
        }
    }];
    
    [operation start];
    
}


-(void)addOperationWithBlockToQueue {
    /**
     输出结果：
     
     2018-07-31 17:54:09.599535+0800 SDemo[39704:1201991] 3 -- <NSThread: 0x60000046d780>{number = 8, name = (null)}
     2018-07-31 17:54:09.599535+0800 SDemo[39704:1201703] 1 -- <NSThread: 0x60000046cc40>{number = 7, name = (null)}
     2018-07-31 17:54:09.599535+0800 SDemo[39704:1201976] 2 -- <NSThread: 0x604000460880>{number = 6, name = (null)}
     2018-07-31 17:54:11.600525+0800 SDemo[39704:1201703] 1 -- <NSThread: 0x60000046cc40>{number = 7, name = (null)}
     2018-07-31 17:54:11.600525+0800 SDemo[39704:1201976] 2 -- <NSThread: 0x604000460880>{number = 6, name = (null)}
     2018-07-31 17:54:11.600571+0800 SDemo[39704:1201991] 3 -- <NSThread: 0x60000046d780>{number = 8, name = (null)}
     
     
     */
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1 -- %@",[NSThread currentThread]);
        }
    }];
    
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2 -- %@",[NSThread currentThread]);
        }
    }];
    
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3 -- %@",[NSThread currentThread]);
        }
    }];
}

/**
 设置最大并发量
 maxConcurrentOperationCount
 默认是 -1 不限制，可进行并发执行； 为 1时，表示队列是串行队列，只能执行串行；大于1时，表示队列为并发队列
 
 */
-(void)setMaxConcurrentOperationCount {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1; //变串行输出
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1 -- %@",[NSThread currentThread]);
        }
    }];
    
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2 -- %@",[NSThread currentThread]);
        }
    }];
    
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3 -- %@",[NSThread currentThread]);
        }
    }];
    
    
}

//添加依赖
-(void)addDependency {
    
    /**
     输出结果:
     
     2018-08-01 10:18:51.828714+0800 SDemo[67418:1830873] 1 -- <NSThread: 0x60400046ce80>{number = 3, name = (null)}
     2018-08-01 10:18:51.828741+0800 SDemo[67418:1829920] 3 -- <NSThread: 0x600000279380>{number = 4, name = (null)}
     2018-08-01 10:18:53.832990+0800 SDemo[67418:1829920] 3 -- <NSThread: 0x600000279380>{number = 4, name = (null)}
     2018-08-01 10:18:53.833033+0800 SDemo[67418:1830873] 1 -- <NSThread: 0x60400046ce80>{number = 3, name = (null)}
     2018-08-01 10:18:55.834838+0800 SDemo[67418:1830873] 2 -- <NSThread: 0x60400046ce80>{number = 3, name = (null)}
     2018-08-01 10:18:57.839001+0800 SDemo[67418:1830873] 2 -- <NSThread: 0x60400046ce80>{number = 3, name = (null)}
     
     */
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1 -- %@",[NSThread currentThread]);
        }
    }];
    
    NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2 -- %@",[NSThread currentThread]);
        }
    }];
    
    NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"3 -- %@",[NSThread currentThread]);
        }
    }];
    
    [block2 addDependency:block3];
    
    [queue addOperation:block1];
    [queue addOperation:block2];
    [queue addOperation:block3];
}

//线程间的通信
-(void)communication {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 2; i ++) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"1 -- %@",[NSThread currentThread]);
        }
        
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            //回到主线程，进行UI刷新操作
            for (int i = 0; i < 2; i ++) {
                [NSThread sleepForTimeInterval:2];
                NSLog(@"2 -- %@",[NSThread currentThread]);
            }
        }];
        
    }];
}

//线程安全和同步性
-(void)thredSafy {
    //火车票售卖的方式 ：场景：总共有50张火车票，有两个售卖火车票的窗口，一个是北京火车票售卖窗口，另一个是上海火车票售卖窗口。两个窗口同时售卖火车票，卖完为止。
    
    
    //没有线程安全
    self.count = 50;
    self.lock = [[NSLock alloc] init];
    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
    queue1.maxConcurrentOperationCount = 1;
    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
    queue2.maxConcurrentOperationCount = 1;
     
    __weak typeof(self) weakSel = self;
    NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
        [weakSel saleTicket];
    }];
    
    NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
        [weakSel saleTicket];
    }];
    
    [queue1 addOperation:block1];
    [queue2 addOperation:block2];
    
}

-(void)saleTicket {
    //不加锁的情况 输出的票数会错乱
//    while (1) {
//        if (self.count > 0) {
//            self.count = self.count - 1;
//            NSLog(@"剩余票数 ： %ld, 窗口是 %@",(long)self.count,[NSThread currentThread]);
//            [NSThread sleepForTimeInterval:0.2];
//        } else {
//            NSLog(@"票卖完了");
//            break;
//        }
//    }
    
    while (1) {
        [self.lock lock];
        if (self.count > 0) {
            
            self.count = self.count - 1;
            NSLog(@"剩余票数 ： %ld, 窗口是 %@",(long)self.count,[NSThread currentThread]);
            [NSThread sleepForTimeInterval:0.2];
            
        }
        [self.lock unlock];
        
        if (self.count <=0) {
            NSLog(@"票卖完了");
            break;
        }
    }
}

-(void)initData {
    // to do

    /**
     基于GCD的面向对象的使用OC语言的封装.  相对于GCD,更简单
     
     一个抽象类 NSOperation
     两个子类  NSInvocationOperation， NSBlockOperation
     
     NSOperation的使用常常是配合NSOperationQueue来进行的。只要是使用NSOperation的子类创建的实例就能添加到NSOperationQueue操作队列之中，一旦添加到队列，操作就会自动异步执行（注意是异步）。如果没有添加到队列，而是使用start方法，则会在当前线程执行。
     
     */
    
    
    /**
    1， NSInvocationOperation
     */
    
//    NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(p_invocationFunc:) object:@"inv1"];
//    //手动执行
//    [invocation start];
//
//    NSInvocationOperation *invocation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(p_invocationFunc2:) object:@"inv2"];
//    NSOperationQueue *q = [[NSOperationQueue alloc] init];
//    [q addOperation:invocation2]; //放在队列中就是异步自动执行
    
//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//    for (int i = 0; i < 20; i ++) {
//        NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(p_invocationFuncMore:) object:[NSString stringWithFormat:@"invocation%d",i]];
//        [queue addOperation:invocation]; //输出无序，说明是并发队列
//    }
    
    
    /**
     2，NSBlockOperation
     和上面的NSInvocationOperation 用法相同， 只是创建方式不同, 可以直接使用block创建
     */
    
//    NSOperationQueue *blockqueue = [[NSOperationQueue alloc] init];
//    for (int i = 0 ; i < 10; i ++) {
//        NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
//            NSLog(@"this is block : %@ -- %d", [NSThread currentThread],i);
//        }];
//        [blockqueue addOperation:block];
//    }
//
//    NSLog(@"finish");
    
    //上面的使用可以简化成下面：
//    NSOperationQueue *blockqueue1 = [[NSOperationQueue alloc] init];
//    for (int i = 0 ; i < 10; i ++) {
//        [blockqueue1 addOperationWithBlock:^{
//            NSLog(@"this is block : %@ -- %d", [NSThread currentThread],i);
//        }];
//    }
    
    /**
    3， 线程间通信
     
     */
   
    
    /**
     4, 高级操作
     有：队列的挂起，队列的取消，添加操作的依赖关系和设置最大并发数。 重点是依赖关系
     */
    
//    self.opQueue.maxConcurrentOperationCount = 2;
//    for (int i = 0 ; i < 10; i ++) {
//        [self.opQueue addOperationWithBlock:^{
//             NSLog(@"this is opQueue : %@ -- %d", [NSThread currentThread],i);
//        }];
//    }
    
    /*
     * 例子
     *
     * 1.下载一个小说压缩包
     *  2.解压缩，删除压缩包
     * 3.更新UI
     */
    NSBlockOperation *block1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"下载压缩包");
    }];
    
    NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"解压压缩包");
    }];
    
    NSBlockOperation *block3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"更新UI 界面");
    }];
    
    [block2 addDependency:block1];
    [block3 addDependency:block2];
    
    //waitUntilFinished  类似GCD中的调度组的通知
    //NO不等待，直接执行输出finish
    //YES等待任务执行完再执行输出finish
    [self.opQueue addOperations:@[block1,block2] waitUntilFinished:YES];
    [[NSOperationQueue mainQueue] addOperation:block3];
    NSLog(@"finish");
    
    
    //暂停 self.opQueue.suspended  取消 [self.opQueue cancelAllOperations];
    
    /** 注意:
     这里的暂停和取消（包括操作的取消和队列的取消）并不代表可以将当前的操作立即取消，而是当当前的操作执行完毕之后不再执行新的操作。
     暂停和取消的区别就在于：暂停操作之后还可以恢复操作，继续向下执行；而取消操作之后，所有的操作就清空了，无法再接着执行剩下的操作。
    
     */

    /**
     GCD是比较底层的封装，我们知道较低层的代码一般性能都是比较高的，相对于NSOperationQueue。所以追求性能，而功能够用的话就可以考虑使用GCD。如果异步操作的过程需要更多的用户交互和被UI显示出来，NSOperationQueue会是一个好选择。如果任务之间没有什么依赖关系，而是需要更高的并发能力，GCD则更有优势。
     高德纳的教诲：“在大概97%的时间里，我们应该忘记微小的性能提升。过早优化是万恶之源。”只有Instruments显示有真正的性能提升时才有必要用低级的GCD。
     */

}


-(void)p_invocationFunc {
//    NSLog(@"this is :%@ -- %@ ", [NSThread currentThread], obj); //这里或输出上面的object
    
    for (int i = 0; i < 2; i ++) {
        [NSThread sleepForTimeInterval:2]; //模拟耗时操作
        NSLog(@"invocation --- %@",[NSThread currentThread]);
    }
}

-(void)p_invocationFunc2:(id)obj {
    NSLog(@"this is :%@ -- %@ ", [NSThread currentThread], obj);
}


-(void)p_invocationFuncMore:(id)obj {
    NSLog(@"this is :%@ -- %@ ", [NSThread currentThread], obj);
}



- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}



@end
